package com.zx.zxpicture.application.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.zx.zxpicture.domain.picture.service.PictureDomainService;
import com.zx.zxpicture.infrstructure.api.aliyunai.AliYunAiApi;
import com.zx.zxpicture.infrstructure.api.aliyunai.model.CreateOutPaintingTaskResponse;

import com.zx.zxpicture.infrstructure.common.BaseResponse;
import com.zx.zxpicture.infrstructure.common.ErrorCode;
import com.zx.zxpicture.infrstructure.common.ResultUtils;
import com.zx.zxpicture.infrstructure.exception.BusinessException;
import com.zx.zxpicture.infrstructure.exception.ThrowUtils;
import com.zx.zxpicture.infrstructure.utils.*;
import com.zx.zxpicture.interfaces.dto.picture.*;
import com.zx.zxpicture.infrstructure.mapper.PictureMapper;
import com.zx.zxpicture.domain.picture.entity.Picture;
import com.zx.zxpicture.domain.space.entity.Space;
import com.zx.zxpicture.domain.user.entity.User;
import com.zx.zxpicture.domain.picture.valueobject.PictureReviewEnum;
import com.zx.zxpicture.interfaces.vo.PictureVO;
import com.zx.zxpicture.interfaces.vo.UserVO;
import com.zx.zxpicture.application.service.PictureApplicationService;
import com.zx.zxpicture.application.service.SpaceApplicationService;
import com.zx.zxpicture.application.service.UserApplicationService;
import com.zx.zxpicture.domain.picture.stratege.DefaultUploadByBatch;
import com.zx.zxpicture.domain.picture.stratege.LargeUploadByBatch;
import com.zx.zxpicture.domain.picture.template.FileUpload;
import com.zx.zxpicture.domain.picture.template.UrlUpload;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.zx.zxpicture.domain.picture.valueobject.PictureConstant.*;

/**
* @author ZX
* @description 针对表【picture(图片)】的数据库操作Service实现
* @createDate 2025-02-19 15:03:21
*/
@Service
@Slf4j
public class PictureApplicationApplicationServiceImpl extends ServiceImpl<PictureMapper, Picture>
    implements PictureApplicationService {

    //使用上传图片的通用服务
    @Resource
   private FileUpload fileUpload;
    @Resource
    private UrlUpload urlUpload;
    @Resource
    private SpaceApplicationService spaceApplicationService;
    @Resource
    private UserApplicationService userApplicationService;
    @Resource
    private PictureDomainService pictureDomainService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    /**
     * 方法事务，可以在方法内部开启事务和提交事务
     */
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private AliYunAiApi aliYunAiApi;

    //本地缓存，使用caffeine缓存框架
    private final Cache<String, String> LOCAL_CACHE =
            Caffeine.newBuilder().initialCapacity(1024)
                    .maximumSize(10000L)
                    // 缓存 5 分钟移除
                    .expireAfterWrite(5L, TimeUnit.MINUTES)
                    .build();

    /**
     * 上传图片
     * @param inputSource 输入方式
     * @param pictureUploadRequest 图片id
     * @param loginUser 用户
     * @return 图片信息
     */
    public PictureVO uploadPicture(Object inputSource, PictureUploadRequest pictureUploadRequest, User loginUser) {
        if(ObjUtil.isEmpty(inputSource)){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"图片数据源不能为空");
        }
        //校验上传空间是否存在
        Long spaceId = pictureUploadRequest.getSpaceId();
        if(spaceId != null){
            //要上传到指定空间
            Space space = spaceApplicationService.getById(spaceId);
            if(space == null){
                throw new BusinessException(ErrorCode.NOT_FOUND_ERROR,"上传空间不存在");
            }
            //修改为SaToken校验权限
//            if(!space.getUserId().equals(loginUser.getId())){
//                throw new BusinessException(ErrorCode.NO_AUTH_ERROR,"没有权限操作该空间");
//            }
            //校验空间额度
            if(space.getTotalCount() > space.getMaxCount()){
                throw new BusinessException(ErrorCode.OPERATION_ERROR,"空间条数不足");
            }
            if(space.getTotalSize() > space.getMaxSize()){
                throw new BusinessException(ErrorCode.OPERATION_ERROR,"空间大小不足");
            }
        }
        //0. 判断是否是更新操作
        //重复上传要更新mysql中图片数据
        Long pictureId = null;
        if(pictureUploadRequest.getId() != null){
            //更新操作
            pictureId = pictureUploadRequest.getId(); //查询老图片
            //重复上传，判断mysql中是否有图片
            Picture picture = getById(pictureId);
            ThrowUtils.throwIf(picture == null, ErrorCode.OPERATION_ERROR, "图片不存在");
            //判断上传空间是否一致
            Long oldSpaceId = picture.getSpaceId();
            if(spaceId == null){
                //两个值一样就表示，开始在公共图库那更新的也在公共图库，开始在私人空间那更新也在私人空间
                spaceId = oldSpaceId;
            }else {
                if(oldSpaceId == null && spaceId != null){
                    throw new BusinessException(ErrorCode.PARAMS_ERROR,"上传的空间和上一次上传的空间不一致");
                }
                if(!oldSpaceId.equals(spaceId)){
                   throw  new BusinessException(ErrorCode.PARAMS_ERROR,"上传的空间和上一次上传的空间不一致");
               }
            }
        }
            //1. 定义上传文件目录
        String uploadPath = null;
        if(spaceId == null){
            //公共图库
            uploadPath = UPLOAD_URL_PREFIX +"/"+loginUser.getId();
        }else {
            //私人空间
            uploadPath = UPLOAD_URL__PRIVATE_PREFIX +"/"+spaceId;
        }
            //2. 上传到cos
            //判断上传方式
        String type = pictureUploadRequest.getType();
        UploadPictureResult uploadPictureResult = null;
            if("url".equals(type)){
                uploadPictureResult = urlUpload.fileUploadPicture(inputSource, uploadPath);
            }else if ("file".equals(type)){
                uploadPictureResult = fileUpload.fileUploadPicture(inputSource, uploadPath);
            }else{
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"上传方式错误");
            }
            //3. 封装数据
            Picture picture = new Picture();
            picture.setUrl(uploadPictureResult.getUrl());
            picture.setThumbnailUrl(uploadPictureResult.getThumbnailUrl());
            //这个返回的是图片上传时的原始名字
            String picName = uploadPictureResult.getPicName();
            if(StrUtil.isNotBlank(pictureUploadRequest.getPicName())){
                picName = pictureUploadRequest.getPicName();
            }
            //设置图片默认分类
            picture.setCategory("默认");
            picture.setName(picName);
            picture.setPicSize(uploadPictureResult.getPicSize());
            picture.setPicWidth(uploadPictureResult.getPicWidth());
            picture.setPicHeight(uploadPictureResult.getPicHeight());
            picture.setPicScale(uploadPictureResult.getPicScale());
            picture.setPicFormat(uploadPictureResult.getPicFormat());
            picture.setUserId(loginUser.getId());
            //设置图片主色调
            picture.setPicColor(FillHexColor.fillHexColor(uploadPictureResult.getPicColor()));
            //设置空间id
            picture.setSpaceId(spaceId);
            Long finalSpaceId = spaceId;
        // 如果 pictureId 不为空，表示更新，否则是新增
        if (pictureId != null) {
                // 如果是更新，需要补充 id 和编辑时间
                picture.setId(pictureId);
                picture.setEditTime(new Date());
                //拿到老图片
                Picture oldPicture = getById(pictureId);
                //异步执行删除老图片
                ExecutorPool.getInstance().execute(() -> {
                    clearPictureFile(oldPicture);
                    log.info("成功删除图片：{},缩略图片：{}",oldPicture.getUrl(),oldPicture.getThumbnailUrl());
                    //释放内存
                    if (finalSpaceId != null) {
                        boolean updated = spaceApplicationService.lambdaUpdate()
                                .eq(Space::getId, finalSpaceId)
                                .setSql("totalCount = totalCount - 1")
                                .setSql("totalSize = totalSize - " + oldPicture.getPicSize())
                                .update();
                        if (!updated) {
                            log.error("空间额度更新失败，spaceId={}", finalSpaceId);
                        }
                    }
                });
            }
            //4.补充审核参数
            fillReviewParams(picture, loginUser);

            //开启事务，保证这两个操作的原子性
            transactionTemplate.execute(status->{
                    //5.保存到数据库
                    boolean b = saveOrUpdate(picture);
                    if(!b){
                        throw new RuntimeException("添加或修改图片失败");
                    }
                    //6.修改个人空间额度
                    if(finalSpaceId != null){
                        spaceApplicationService.lambdaUpdate()
                                .eq(Space::getId, finalSpaceId)
                                .setSql("totalCount = totalCount + 1")
                                .setSql("totalSize = totalSize +" + picture.getPicSize())
                                .update();
                    }
                    return picture;
            });
            //7.返回结果
            return PictureVO.objToVo(picture);
    }

    /**
     * 构造分页查询条件
     * @param pictureQueryRequest
     * @return
     */
    public QueryWrapper<Picture> getQueryWrapper(PictureQueryRequest pictureQueryRequest) {
      return pictureDomainService.getQueryWrapper(pictureQueryRequest);
    }

    /**
     * 封装图片返回类
     * @param picture
     * @param request
     * @return
     */
    @Override
    public PictureVO getPictureVO(Picture picture, HttpServletRequest request) {
        // 对象转封装类
        PictureVO pictureVO = PictureVO.objToVo(picture);
        // 关联查询用户信息
        Long userId = picture.getUserId();
        if (userId != null && userId > 0) {
            User user = userApplicationService.getById(userId);
            UserVO userVO = userApplicationService.getUserVO(user);
            pictureVO.setUser(userVO);
        }
        return pictureVO;
    }

    /**
     * 分页获取图片封装
     */
    @Override
    public Page<PictureVO> getPictureVOPage(Page<Picture> picturePage, HttpServletRequest request) {
        List<Picture> pictureList = picturePage.getRecords();
        //创建新的分页对象
        Page<PictureVO> pictureVOPage = new Page<>(picturePage.getCurrent(), picturePage.getSize(), picturePage.getTotal());
        if(pictureList.isEmpty()){
            pictureVOPage.setRecords(List.of());
            return pictureVOPage;
        }

        // 对象列表 => 封装对象列表
        List<PictureVO> pictureVOList = pictureList.stream().map(PictureVO::objToVo).collect(Collectors.toList());
        //遍历图片集合，整合用户id
        Set<Long> userIds = pictureList.stream().map(Picture::getUserId).collect(Collectors.toSet());
        //查询所有用户数据
        Map<Long, List<User>> usersMap = userApplicationService.listByIds(userIds).stream().collect(Collectors.groupingBy(User::getId));
        //填充用户信息
        pictureVOList.forEach(pictureVO -> {
            Long userId = pictureVO.getUserId();
            User user = null;
            if (usersMap.containsKey(userId)) {
                user = usersMap.get(userId).get(0);
            }
            pictureVO.setUser(userApplicationService.getUserVO(user));
        });
        pictureVOPage.setRecords(pictureVOList);
        return pictureVOPage;
    }


    /**
     * 管理员审批用户图片
     * @param pictureReviewRequest 审批图片请求
     * @param loginUser 审批用户
     */
    @Override
    public void doPictureReview(PictureReviewRequest pictureReviewRequest, User loginUser) {
        //1.获取审批参数
        Long id = pictureReviewRequest.getId(); //图片id
        PictureReviewEnum pictureReviewEnum = PictureReviewEnum.getEnumByValue(pictureReviewRequest.getReviewStatus());
        String reviewMessage = pictureReviewRequest.getReviewMessage();
        if(id == null || pictureReviewEnum == null || pictureReviewEnum.equals(PictureReviewEnum.REVIEWING)){
            //审批结果不能为待审批，必须拒绝或者通过
            throw  new BusinessException(ErrorCode.PARAMS_ERROR,"审批结果必须拒接或者通过");
        }
        //2.获取老图片
        Picture picture = getById(id);
        if(ObjUtil.isNull(picture)){
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"图片不存在");
        }
        //3.更新图片审核状态
        Picture newPicture = new Picture();
        if(pictureReviewEnum.equals(PictureReviewEnum.PASS)){
            reviewMessage = "管理员审核通过";
        }
        newPicture.setId(id);
        newPicture.setReviewStatus(pictureReviewEnum.getValue());
        newPicture.setReviewerId(loginUser.getId());
        newPicture.setReviewMessage(reviewMessage);
        newPicture.setReviewTime(new Date());
        boolean b = updateById(newPicture);
        if(!b){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"更新图片审核失败");
        }
    }

    /**
     * 填充图片审核参数
     * @param picture
     * @param loginUser
     */
    public void fillReviewParams(Picture picture,User loginUser){
        if(userApplicationService.isAdmin(loginUser)){
            picture.setReviewStatus(PictureReviewEnum.PASS.getValue());
            picture.setReviewMessage("管理员自动过审");
            picture.setReviewerId(loginUser.getId());
            picture.setReviewTime(new Date());
           return;
        }
        //如果是用户就是直接待审核
        picture.setReviewStatus(PictureReviewEnum.REVIEWING.getValue());
    }

    /**
     * 批量抓取和创建图片
     * @param pictureUploadByBatchRequest 分页抓取请求
     * @param loginUser 抓取用户
     * @return 成功创建的图片数
     */
    @Override
    public Integer uploadPictureByBatch(PictureUploadByBatchRequest pictureUploadByBatchRequest, User loginUser) {
        //使用策略模式
        Integer count = pictureUploadByBatchRequest.getCount();
        if(count > 30){
            return new LargeUploadByBatch(this).uploadPictureByBatch(pictureUploadByBatchRequest, loginUser);
        }else{
            return new DefaultUploadByBatch(this).uploadPictureByBatch(pictureUploadByBatchRequest, loginUser);
        }
    }
    /**
     * 通过多级缓存分页查询图片
     * @param pictureQueryRequest 分页查询请求
     * @param request 网页请求
     * @return
     */
    @Override
    public Page<PictureVO> listPictureVOByPageWithCache(PictureQueryRequest pictureQueryRequest, HttpServletRequest request){
        //1.查询本地缓存
        String localCacheKey = "zxpicture:listPictureVOByPage:";
        localCacheKey = localCacheKey + DigestUtils.md5DigestAsHex(JSONUtil.toJsonStr(pictureQueryRequest).getBytes());
        // 从本地缓存中查询
        String localCacheValue = LOCAL_CACHE.getIfPresent(localCacheKey);
        if (localCacheValue != null) {
            // 如果缓存命中，返回结果
            return JSONUtil.toBean(localCacheValue, Page.class);
        }
        //2.查询redis缓存
        ValueOperations<String, String> stringOption = stringRedisTemplate.opsForValue();
        //将查询请求转换为JSON字符创
        String queryCondition = JSONUtil.toJsonStr(pictureQueryRequest);
        //因为jsonStr很长，所以使用哈希算法对json字符创进行哈希，获得它的md5值作为key，减少了存储空间
        String hashKey = DigestUtils.md5DigestAsHex(queryCondition.getBytes());
        String key = String.format("zxpicture:listPictureVOByPage:%s", hashKey);
        String redisCacheValue = stringOption.get(key);
        if(redisCacheValue != null){
            //缓存命中。反序列化返回数据
            return JSONUtil.toBean(redisCacheValue, Page.class);
        }
        //获取分布式锁
        //用查询条件作为key，这样不同的查询条件就不会冲突，都能拿到对应的锁去写数据
        String lockKey = String.format("zxpicture:overwriteData:%s", hashKey);
        //用uuid加线程的id作为锁的值
        String lockValue = UUID.randomUUID().toString() + Thread.currentThread().getId();
        while (true){
            if(Boolean.TRUE.equals(RedisLockUtil.redisTryLock(stringRedisTemplate,lockKey, lockValue, 10, TimeUnit.SECONDS))){
                try {
                    //todo 二次判断缓存中是否有值
                    //获取锁成功，执行业务逻辑
                    //缓存未命中，查询数据库
                    //3.查询数据库
                    long current = pictureQueryRequest.getCurrent();
                    long size = pictureQueryRequest.getPageSize();
                    // 限制爬虫
                    ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
                    // 普通用户默认只能查看已过审的数据
                    pictureQueryRequest.setReviewStatus(PictureReviewEnum.PASS.getValue());
                    // 分页查询数据库
                    Page<Picture> picturePage = this.page(new Page<>(current, size),
                            this.getQueryWrapper(pictureQueryRequest));
                    Page<PictureVO> pictureVOPage = this.getPictureVOPage(picturePage, request);
                    //防止缓存穿透
                    if(CollUtil.isEmpty(picturePage.getRecords()) || picturePage.getTotal() == 0){
                        //mysql没有这个数据，往redis中写入空数据
                        stringOption.set(key,"");
                    }
                    //4.写入本地缓存
                    String cacheValue = JSONUtil.toJsonStr(pictureVOPage);
                    LOCAL_CACHE.put(localCacheKey, cacheValue);
                    //5.写入redis
                    //随机过期时间，防止缓存雪崩
                    long timeout = 5 + RandomUtil.randomLong(0,5);
                    stringOption.set(key,cacheValue,timeout, TimeUnit.MINUTES);
                    // 返回
                    return pictureVOPage;
                }finally {
                    //6释放分布式锁
                    if(RedisLockUtil.unLock(stringRedisTemplate, lockKey, lockValue) == 1){
                        log.error("释放分布式锁失败，线程运行超过了锁的自动释放时间");
                    }
                }
            }else{
                //获取锁失败，重试
                // 从本地缓存中查询
                String localValue = LOCAL_CACHE.getIfPresent(localCacheKey);
                if (localValue != null) {
                    // 如果缓存命中，返回结果
                    return JSONUtil.toBean(localValue, Page.class);
                }
                //查询redis中的缓存
                String redisValue = stringOption.get(key);
                if(redisValue != null){
                    //缓存命中。反序列化返回数据
                    return JSONUtil.toBean(redisValue, Page.class);
                }
                //睡一会
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    /**
     * 用户编辑图片
     * @param pictureEditRequest
     * @param loginUser
     */
    @Override
    public void editPicture(PictureEditRequest pictureEditRequest, User loginUser) {
        pictureDomainService.editPicture(pictureEditRequest, loginUser);
    }


    /**
     * 删除图片
     * @param pictureId
     * @param loginUser
     */
    @Override
    public void deletePicture(long pictureId, User loginUser) {
        //查询老图片
        Picture oldPicture = getById(pictureId);
        ThrowUtils.throwIf(oldPicture == null, ErrorCode.NOT_FOUND_ERROR);
        //改用saToken校验，校验权限
//        checkPictureAuth(loginUser, oldPicture);
        // 操作数据库删除图片
        boolean result = removeById(pictureId);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        Long spaceId = oldPicture.getSpaceId();
        // 修改空间额度
        if (spaceId != null) {
            boolean updated = spaceApplicationService.lambdaUpdate()
                    .eq(Space::getId, spaceId)
                    .setSql("totalCount = totalCount - 1")
                    .setSql("totalSize = totalSize - " + oldPicture.getPicSize())
                    .update();
            if (!updated) {
                log.error("空间额度更新失败，spaceId={}", spaceId);
            }
        }
        //成功删除了数据再去清理COS中的文件，不然可能会数据不一致
        //异步执行
        ExecutorPool.getInstance().execute(() -> {
            try {
                clearPictureFile(oldPicture);
                log.info("成功删除图片：{}, 缩略图：{}", oldPicture.getUrl(), oldPicture.getThumbnailUrl());
            } catch (Exception e) {
                log.error("异步任务执行失败：", e);
            }
        });
    }

    /**
     * 删除COS中的压缩图和缩略图
     * @param oldPicture 压缩图
     */
    @Override
    public void clearPictureFile(Picture oldPicture) {
        pictureDomainService.clearPictureFile(oldPicture);
    }

    /**
     * 根据颜色查询主色调相近的图片
     * @param spaceId 空间id
     * @param picColor 目标主色调 16进制
     * @param loginUser 用户
     * @return 图片列表（颜色越相近排名越靠前）
     */
    @Override
    public List<PictureVO> searchPictureVOByColor(Long spaceId, String picColor, User loginUser) {
        return pictureDomainService.searchPictureVOByColor(spaceId, picColor, loginUser);
    }

    /**
     * 批量编辑图片
     * @param pictureEditByBatchRequest 编辑图片请求
     * @param loginUser
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editPictureByBatch(PictureEditByBatchRequest pictureEditByBatchRequest, User loginUser) {
        List<Long> pictureIdList = pictureEditByBatchRequest.getPictureIdList();
        Long spaceId = pictureEditByBatchRequest.getSpaceId();
        String category = pictureEditByBatchRequest.getCategory();
        List<String> tags = pictureEditByBatchRequest.getTags();
        // 1. 校验参数
        ThrowUtils.throwIf(CollUtil.isEmpty(pictureIdList), ErrorCode.PARAMS_ERROR);
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NO_AUTH_ERROR);
        //2.校验空间权限
        if(spaceId == null){
            //批量操作公共图库，只有管理员有权限
            if(!userApplicationService.isAdmin(loginUser)){
                throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
            }
        }else {
            //私人空间，只有空间管理员有权限
            Space space = spaceApplicationService.getById(spaceId);
            ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR, "空间不存在");
            if (!loginUser.getId().equals(space.getUserId())) {
                throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "没有空间访问权限");
            }
        }
        //3.查询需要修改的图片，仅查询需要的字段（提高性能）
        List<Picture> pictureList = lambdaQuery()
                //指定查询字段
                .select(Picture::getId, Picture::getSpaceId)
                .eq(spaceId != null, Picture::getSpaceId, spaceId)
                .isNull(spaceId == null, Picture::getSpaceId)
                .in(Picture::getId, pictureIdList)
                .list();
        if (pictureList.isEmpty()) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "指定的图片不存在或不属于该空间");
        }
        //4.更新分类和标签
        for (Picture picture : pictureList) {
            if(StrUtil.isNotBlank(category)){
                picture.setCategory(category);
            }
            if(CollUtil.isNotEmpty(tags)){
                picture.setTags(JSONUtil.toJsonStr(tags));
            }
        }
        //5.批量重命名
        String nameRule = pictureEditByBatchRequest.getNameRule();
        fillPictureWithNameRule(pictureList, nameRule);

        //6.批量更新图片
        //默认更新属性值不为空的，所有上面的sql优化就大大的提高了性能，在这里也只更新真正需要更新的字段
        //不会将不需要更新的字段再重写一遍
        boolean result = updateBatchById(pictureList);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
    }

    /**
     * 动态填充值
     * 格式：图片{序号}
     * {序号} 填充为 1 2 ...
     * @param pictureList 图片列表
     * @param nameRule 命名规则 {序号}
     */
    private void fillPictureWithNameRule(List<Picture> pictureList, String nameRule) {
        if(StrUtil.isBlank(nameRule)){
            //不需要重命名
            return;
        }
        long count = 1;
        try {
            for (Picture picture : pictureList) {
                //使用正则替换命名规则中的所有{序号}
                picture.setName(nameRule.replaceAll("\\{序号}", String.valueOf(count++)));
            }
        }catch (Exception e){
            log.error("命名规则解析错误",e);
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"命名规则解析错误");
        }

    }


    /**
     * 线程池加并发批量编辑图片分类，标签和重命名（同时处理上万图片时可以使用）
     */
    @Transactional(rollbackFor = Exception.class)
    //有一个任务失败，所有数据回滚
    public void batchEditPictureMetadata(PictureEditByBatchRequest pictureEditByBatchRequest, Long spaceId, User loginUser) {
        List<Long> pictureIdList = pictureEditByBatchRequest.getPictureIdList();
        String category = pictureEditByBatchRequest.getCategory();
        List<String> tags = pictureEditByBatchRequest.getTags();
        // 1. 校验参数
        ThrowUtils.throwIf(CollUtil.isEmpty(pictureIdList), ErrorCode.PARAMS_ERROR);
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NO_AUTH_ERROR);
        //2.校验空间权限
        if(spaceId == null){
            //批量操作公共图库，只有管理员有权限
            if(!userApplicationService.isAdmin(loginUser)){
                throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
            }
        }else {
            //私人空间，只有空间管理员有权限
            Space space = spaceApplicationService.getById(spaceId);
            ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR, "空间不存在");
            if (!loginUser.getId().equals(space.getUserId())) {
                throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "没有空间访问权限");
            }
        }

        // 查询空间下的图片
        List<Picture> pictureList = this.lambdaQuery()
                //指定查询字段
                .select(Picture::getId, Picture::getSpaceId)
                .eq(spaceId != null, Picture::getSpaceId, spaceId)
                .isNull(spaceId == null, Picture::getSpaceId)
                .in(Picture::getId, pictureIdList)
                .list();

        if (pictureList.isEmpty()) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "指定的图片不存在或不属于该空间");
        }

        // 获取全局线程池
        ThreadPoolExecutor executor = ExecutorPool.getInstance().getExecutor();

        // 分批处理避免长事务
        int batchSize = 100;
        List<CompletableFuture<Void>> futures = new ArrayList<>();
        for (int i = 0; i < pictureList.size(); i += batchSize) {
            //每个线程操作的图片列表数据
            List<Picture> batch = pictureList.subList(i, Math.min(i + batchSize, pictureList.size()));

            // 异步处理每批数据
            //这个集合是记录这些每个任务的执行状态
            //runAsync没有返回值
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                batch.forEach(picture -> {
                    // 编辑分类和标签
                    if (pictureEditByBatchRequest.getCategory() != null) {
                        picture.setCategory(pictureEditByBatchRequest.getCategory());
                    }
                    if (pictureEditByBatchRequest.getTags() != null) {
                        picture.setTags(String.join(",", pictureEditByBatchRequest.getTags()));
                    }
                });
                //重命名
                String nameRule = pictureEditByBatchRequest.getNameRule();
                fillPictureWithNameRule(batch, nameRule);
                //插入数据库
                boolean result = this.updateBatchById(batch);
                if (!result) {
                    throw new BusinessException(ErrorCode.OPERATION_ERROR, "批量更新图片失败");
                }
                //将所有任务交给这个线程池执行
            }, executor);
            //将当前任务的执行结果状态添加到集合中
            futures.add(future);
        }

        // 等待所有任务完成
        //如果任务以异常结束，join() 会抛出 CompletionException，封装原始异常。
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
    }

    /**
     * 创建扩图任务
     * @param request 创建扩图任务请求参数
     * @return 创建扩图任务响应结果
     */
    public CreateOutPaintingTaskResponse createOutPaintingTask(CreatePictureOutPaintingTaskRequest request, User loginUser){
       return pictureDomainService.createOutPaintingTask(request, loginUser);
    }

    @Override
    public void validPicture(Picture picture) {
        picture.validPicture();
    }

    @Override
    public BaseResponse<PictureTagCategory> listPictureTagCategory() {
        PictureTagCategory pictureTagCategory = new PictureTagCategory();
        List<String> tagList = Arrays.asList("热门", "搞笑", "生活", "高清", "艺术", "校园", "背景", "简历", "创意");
        List<String> categoryList = Arrays.asList("模板", "电商", "表情包", "素材", "海报");
        pictureTagCategory.setTagList(tagList);
        pictureTagCategory.setCategoryList(categoryList);
        return ResultUtils.success(pictureTagCategory);
    }
}




